package nbcp.web.open

import io.swagger.annotations.Api
import io.swagger.annotations.ApiOperation
import io.swagger.annotations.ApiParam
import io.swagger.annotations.ApiResponse
import nbcp.base.TimeSpan
import org.springframework.beans.factory.annotation.Value
import org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext
import org.springframework.web.bind.annotation.GetMapping
import org.springframework.web.bind.annotation.RequestMapping
import org.springframework.web.bind.annotation.RequestMethod
import org.springframework.web.bind.annotation.RestController
import org.springframework.web.servlet.ModelAndView
import nbcp.base.comm.*

import nbcp.base.extend.*
import nbcp.comm.JsonpMapping
import nbcp.db.mongo.entity.SysUser
import nbcp.db.mongo.repository.sys.findByMobile
import nbcp.db.sys.doLogin

import nbcp.extend.debugServerInfo
import nbcp.web.ClientIp
import org.springframework.beans.factory.annotation.Autowired
import springfox.documentation.annotations.ApiIgnore
import java.io.File
import java.time.LocalDate
import java.time.LocalDateTime
import java.util.*
import javax.servlet.http.HttpServletRequest
import javax.servlet.http.HttpSession
import nbcp.base.*;
import nbcp.base.utils.*
import nbcp.db.IdName
import nbcp.db.mongo.*
import nbcp.db.mongo.entity.SysLoginUser
import nbcp.db.mongo.entity.VersionData
import nbcp.db.sys.doRegiste
import nbcp.web.UserId
import nbcp.web.UserName
import org.springframework.web.client.RestTemplate
import kotlin.math.absoluteValue
import kotlin.math.sign


/**
 * Created by udi on 17-5-23.
 */
@OpenAction
@RestController
@JsonpMapping("")
class Open_Home_Controller {

    @Value("\${spring.profiles.active}")
    var profiles: String = "";

    companion object {
        var AppId = "39a578f5-f06b-49c8-928c-c3a2e17d6c9d";
        var AppKey = "928cc3a2e17d6c9d";
    }

    @ApiOperation("获取最新版本信息", notes = "如果没有返回内容，说明数据库中没有数据，客户端不必处理。", tags = arrayOf("open"))
    @RequestMapping("/version", method = arrayOf(RequestMethod.GET))
    fun version(
            @ApiParam("客户端: android,ios") client: String,
            @ApiParam("客户端版本号，如果传递了无效的版本号， 则返回的最新版本信息是强制更新的。") version: String,
            request: HttpServletRequest): ApiResult<VersionData> {

        var client_type = AppClientEnum::class.java.GetEnumList().firstOrNull { it.toString().toLowerCase() == client.toLowerCase() };

        if (client_type == null) {
            return ApiResult("不识别的客户端!")
        }

        var updateVersion = mor.system.versionData.query()
                .where { it.client match client_type }
                .orderBy { it.publishAt.desc }
                .toEntity()

        if (updateVersion == null) {
            return ApiResult()
        }

        var versionInfo = mor.system.versionData.query()
                .where { it.client match client_type }
                .where { it.version match version }
                .toEntity();
        if (versionInfo == null) {
            updateVersion.mustUpdate = true;
            return ApiResult.of(updateVersion)
        }


        if (updateVersion.mustUpdate == false) {
            var mustUpdate = mor.system.versionData.query()
                    .where { it.client match client_type }
                    .where { it.mustUpdate match true }
                    .where { it.publishAt match_greaterThan versionInfo.publishAt }
                    .exists();

            updateVersion.mustUpdate = mustUpdate;
        }

        return ApiResult.of(updateVersion);
    }


    data class PasswordModel(
            //未加密的密码。
            var password: String = "",
            var time: Long = 0L
    ) {
        fun encrypt(): String {
            return AESUtils.encrypt("""{"password":"${password}","time":${time}}""", AppKey)
        }
    }

    @ApiIgnore
    @RequestMapping("/hi", method = arrayOf(RequestMethod.GET, RequestMethod.POST))
    fun hi(request: HttpServletRequest, session: HttpServletRequest): ApiResult<JsonMap> {
        var ret = ApiResult<JsonMap>()
        ret.msg = "";
        ret.data = JsonMap();

        var jarFile = MyUtil.getStartingJarFile();
        ret.data!!["发版时间"] = Date(jarFile.lastModified()).AsString();
        ret.data!!["application"] = (SpringUtil.context as ServletWebServerApplicationContext).applicationName
        ret.data!!["当前配置"] = (SpringUtil.context as ServletWebServerApplicationContext).debugServerInfo;
        ret.data!!["登录用户Id"] = session.UserId;
        ret.data!!["登录用户名称"] = session.UserName;
        ret.data!!["服务器时间"] = LocalDateTime.now().AsString();
        ret.data!!["时区"] = TimeZone.getDefault().displayName;

        return ret;
    }


//    @ApiIgnore
//    @RequestMapping("/admin", method = arrayOf(RequestMethod.GET, RequestMethod.POST))
//    fun admin(request: HttpServletRequest, session: HttpServletRequest): ApiResult<*>? {
//        var ret = SpringUtil.getBean<admin>().hi()
//        return ret;
//    }


    @ApiOperation("退出登录", tags = arrayOf("open"))
    @JsonpMapping("/logout")
    fun logout(session: HttpSession, request: HttpServletRequest): JsonResult {
        session.invalidate();

//        var openId = request.getHeader("wxoid");
//        myRequest.session.getAttribute("PzxSession")
//        rer.sys.pzxSession.delete(openId.AsString(request.requestedSessionId))

        return JsonResult()
    }

    @ApiOperation("登录", tags = arrayOf("open"),
            notes = """"保持会话：
客户端拦截 Response Header，当有 :Set-Cookie 段时，把 JSESSIONID 的值取出来,保存到客户端,之后请求时,放入到 Request Header 的Cookie 中，模拟Web Session：
拦截内容形式:
Set-Cookie: JSESSIONID=XXXX; path=/; Max-Age=259200; Expires=Mon, 24-Dec-2018 07:34:58 GMT

每次请求时, Request Header 中添加 Cookie:
Cookie: JSESSIONID=XXXX
""")
    @JsonpMapping("/login")
    fun login(@ApiParam("登录名或手机号") loginName: String,
              @ApiParam("加密之后的密码") password: String,
              request: HttpServletRequest,
              session: HttpSession): ApiResult<SysUser> {

//        var password_json = AESUtils.decrypt(password, AppKey).FromJson<PasswordModel>()

//        if (password_json.time != time) {
//            return ApiResult("时间戳不一致", "time")
//        }
//
//        if (LocalDateTime.now().minus(Date(time).AsLocalDateTime()!!).totalMinutes.absoluteValue > 15) {
//            return ApiResult("时间戳失效", "time")
//        }

//        var password = password_json.password;

        var userResult = mor.system.sysUser.doLogin(loginName, password, request);
        if (userResult.msg.HasValue) {
            return ApiResult(userResult.msg)
        }

        return userResult;
    }


    @JsonpMapping("/registe")
    @ApiOperation("注册用户", tags = arrayOf("open"))
    fun registe(@ApiParam("登录名") loginName: String,
                @ApiParam("手机号") mobile: String,
                @ApiParam("手机验证码") checkCode: String,
                @ApiParam("-邮箱") email: String,
                @ApiParam("加密后的密码") password: String,
                @ApiParam("客户端加密密码时的时间戳") time: Long
    ): JsonResult {
        if (checkCode == "") {
            return JsonResult("手机验证码不能为空！", "checkCode")
        }
        var password_json = AESUtils.decrypt(password, AppKey).FromJson<PasswordModel>()

        if (password_json.time != time) {
            return JsonResult("时间戳不一致", "time")
        }

        if (LocalDateTime.now().minus(Date(time).AsLocalDateTime()!!).totalMinutes.absoluteValue > 15) {
            return JsonResult("时间戳失效", "time")
        }

        var password = password_json.password;

        var msg = ValidateUtil.password_validate(password);
        if (msg.HasValue) {
            return JsonResult(msg);
        }

        if (mor.system.sysUser.query()
                        .where { it.loginName match loginName }.exists()) {
            return JsonResult("用户名${loginName}已存在！", "loginName")
        }

        if (mor.system.sysUser.query()
                        .where { it.mobile match mobile }.exists()) {
            return JsonResult("手机号${mobile}已注册！", "mobile")
        }

        var db_mobile_code = mor.system.mobileCodeLog.query()
                .where { it.mobile match mobile }
                .where { it.module match MobileCodeModuleEnum.Registe }
                .where { it.isUsed match false }
                .where { it.errorMessage match "" }
                .orderBy { it.id.desc }
                .toEntity();

        if (db_mobile_code == null) {
            return JsonResult("请先获取手机验证码！", "checkCode")
        }

        if (db_mobile_code.code != checkCode) {
            return JsonResult("手机验证码错误！", "checkCode")
        }

        var ret = mor.system.sysUser.doRegiste(loginName, mobile, email, password);


        mor.system.mobileCodeLog.update()
                .where { it.mobile match db_mobile_code.mobile }
                .where { it.module match db_mobile_code.module }
                .where { it.isUsed match false }
                .set { it.isUsed to true }
                .exec()

        return ret;
    }


    data class SmsSignData(
            var mobile: String,
            var time: Long
    ) {
        val appId: String = Open_Home_Controller.AppId;

        fun getMd5SignCode(): String {
            return Md5Util.getMd5("""{"mobile":"${mobile}","appId":"${appId}","time":${time}}""");
        }
    }


    @ApiOperation("发送验证码", tags = arrayOf("open"))
    @JsonpMapping("/sendSmsCode")
    fun sendSmsCode(mobile: String,
                    @ApiParam("生成签名的时间戳") time: Long,
                    @ApiParam("使用的模块: Registe-注册，ChangeMobile-更换手机，ForgetPassword-找回密码，BindBankCard-绑定银行卡") module: MobileCodeModuleEnum,
                    @ApiParam("签名，签名方法参考文档") signCode: String
    ): JsonResult {

        if (mobile.length != 11) {
            return JsonResult("手机号格式不正确", "mobile")
        }

        if (signCode.isEmpty()) {
            return JsonResult("签名不能为空", "signCode")
        }

        if (LocalDateTime.now().minus(Date(time).AsLocalDateTime()!!).totalMinutes.absoluteValue > 15) {
            return JsonResult("时间戳验证失败", "time")
        }

        if (signCode != SmsSignData(mobile, time).getMd5SignCode()) {
            return JsonResult("非法签名", "signCode")
        }

        var maxItem = 20;

        var log = mor.system.mobileCodeLog.query()
                .where { it.mobile match mobile }
                .where { it.module match module }
                .where { it.createAt match_gte LocalDate.now() }
                .orderBy { it.id.desc }
                .limit(0, maxItem)
                .toList()


        if (log.any()) {
            if (log.size >= maxItem) {
                return JsonResult("当天下发短信已超过 ${maxItem} 条！");
            }

            if (LocalDateTime.now().minus(log.first().createAt).totalSeconds < 60) {
                return JsonResult("短信过于频繁，请稍后再试！");
            }

            if (log.last().isUsed == false && LocalDateTime.now().minus(log.last().createAt).totalMinutes < 5) {
                return JsonResult("已下发过验证码，请使用最后一个验证码！");
            }
        }

        var code = MyUtil.getRandomWithLength(6);
//        smsService.sendSms(module, mobile, code);

        return JsonResult();
    }


    @JsonpMapping("/forgetPassword")
    @ApiOperation("忘记密码", tags = arrayOf("open"))
    fun forgetPassword(
            mobile: String,
            @ApiParam("客户端加密密码的时间戳") time: Long,
            @ApiParam("手机验证码") checkCode: String,
            @ApiParam("加密后的密码,见接口文档") password: String
    ): JsonResult {
        if (password.isEmpty()) {
            return JsonResult("密码不能为空", "password")
        }
        if (checkCode.isEmpty()) {
            return JsonResult("手机验证码不能为空", "checkCode")
        }

        var password_json = AESUtils.decrypt(password, AppKey).FromJson<PasswordModel>()

        if (password_json.time != time) {
            return JsonResult("时间戳不一致", "time")
        }

        if (LocalDateTime.now().minus(Date(time).AsLocalDateTime()!!).totalMinutes.absoluteValue > 15) {
            return JsonResult("时间戳失效", "time")
        }

        var password = password_json.password;

        if (password.length < 6) {
            return JsonResult("密码不能小于6位！", "password")
        }

        var db_mobile_code = mor.system.mobileCodeLog.query()
                .where { it.mobile match mobile }
                .where { it.module match MobileCodeModuleEnum.ForgetPassword }
                .where { it.isUsed match false }
                .where { it.errorMessage match "" }
                .orderBy { it.id.desc }
                .toEntity();


        if (db_mobile_code == null) {
            return JsonResult("请先获取手机验证码！", "checkCode")
        }

        if (db_mobile_code.code != checkCode) {
            return JsonResult("手机验证码错误！", "checkCode")
        }

        password = Md5Util.getBase64Md5(password)

        var loginName = mor.system.sysUser.query()
                .where { it.mobile match mobile }
                .select { it.loginName }
                .toEntity(String::class.java)

        if (loginName.HasValue == false) {
            return JsonResult("找不到用户信息", "mobile")
        }

        mor.system.sysLoginUser.update()
                .where { it.loginName match loginName }
                .set { it.password to password }
                .exec();

        if (mor.affectRowCount == 0) {
            return JsonResult("设置密码失败")
        }

        mor.system.mobileCodeLog.update()
                .where { it.mobile match db_mobile_code.mobile }
                .where { it.module match MobileCodeModuleEnum.ForgetPassword }
                .where { it.isUsed match false }
                .set { it.isUsed to true }
                .exec()

        return JsonResult()
    }

}